package logic

import (
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/models/entity"
	"core/repo"
	"core/service"
	"encoding/json"
	"framework/msError"
	"framework/remote"
	"github.com/shopspring/decimal"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"hall/component/proto"
	"hall/models/response"
	"strconv"
	"sync"
	"time"
)

const (
	MaxOrderPayCancel = 11 * 60 // 自动取消订单时间
	MaxOrderPay       = 10 * 60 // 订单可操作时间
	MaxGoodsWeb       = 10 * 60 // 用户访问时间心跳
)

type GoodsManager struct {
	sync.RWMutex
	Users         map[uint]*entity.User
	schedules     map[primitive.ObjectID]*time.Timer
	schedulesUser map[uint]*time.Timer
	orderCh       chan *proto.GoodsRule
	goodsService  *service.GoodsService
	userService   *service.UserService
	walletService *service.WalletService
	manager       *Manager
}

// GetGoodsUser 查询商城用户 无 添加一个用户数据
func (m *GoodsManager) GetGoodsUser(uid uint, userService *service.UserService) (*entity.User, *msError.Error) {
	m.Lock()
	defer m.Unlock()
	if len(m.Users) <= 0 {
		m.Users = make(map[uint]*entity.User)
	}
	user, ok := m.Users[uid]
	var err error
	user, err = userService.FindUserByUid(context.TODO(), uid)
	if err != nil {
		logs.Error("[Goods]用户%d err:获取用户数据失败 req=%v,err=%v", uid, err)
		return nil, biz.SqlError
	}
	m.Users[uid] = user
	//创建一个心跳
	if ok {
		// 如果用户存在 更新心跳定时任务
		m.schedulesUser[uid].Reset(MaxGoodsWeb * time.Second)
		return user, nil
	}
	go m.scheduleUserExecute(user)
	return user, nil
}

func (m *GoodsManager) CreateOrder(uid, gid uint, session *remote.Session) (*entity.GoodsOrder, *msError.Error) {
	//1.查询订单
	ctx := context.TODO()
	order := m.goodsService.OrderInfo(ctx, MaxOrderPay, uid, gid)
	if order.OrderNo == "" {
		//2.无订单 查询商品
		g := m.goodsService.GetGoods(ctx, uid, gid)
		if g == nil || g.ID == 0 {
			logs.Error("[Goods]用户%d Order err:未获取商品数据 req=%v", uid, g)
			return nil, biz.GoodsError
		}
		if g.Remain <= 0 {
			logs.Error("[Goods]用户%d Order err:该商品剩余数量不足 req=%v", uid, g)
			return nil, biz.GoodsSoldOutError
		}
		if g.RestrictNum == 0 {
			restrictedPurchase := m.goodsService.ConfigGetVal(ctx, "restricted_purchase")
			fpString, _ := decimal.NewFromString(restrictedPurchase)
			fpStringData := fpString.IntPart()
			count := m.goodsService.BuyOrderCount(ctx, uid, g.ID, fpStringData)
			if count >= g.RestrictNum {
				logs.Error("[Goods]用户%d Order err:该商品为限量商品 req=%v", uid, g)
				return nil, biz.RestrictError
			}
		}
		PayMethod := make([]entity.GoodsPayMethod, 0)
		for _, v := range g.GoodsPay {
			pay := entity.GoodsPayMethod{
				TypeID:     v.TypeID,
				TypeName:   v.Types.Name,
				TypeImg:    v.Types.Img,
				TypeModule: v.Types.Module,
				Num:        v.Num,
			}
			PayMethod = append(PayMethod, pay)
		}

		//3.向orderCh写入订单
		goodsRule := new(proto.GoodsRule)
		goodsRule.GoodsOrder = &entity.GoodsOrder{
			UID:        uid,
			GID:        gid,
			GoodsType:  g.Type,
			TypeID:     g.TypeID,
			TypeName:   g.Types.Name,
			TypeImg:    g.Types.Img,
			TypeModule: g.Types.Module,
			TypeType:   g.Types.Type,
			OrderNo:    utils.GenOrderNumber("GOODS"),
			Price:      g.Price,
			PayMethod:  PayMethod,
			CreateTime: time.Now().Unix(),
		}
		goodsRule.Session = session
		m.orderCh <- goodsRule
	}
	return order, nil
}

func (m *GoodsManager) run() {
	//订单处理
	go m.orderChHandler()
	//重启任务，未支付订单自动退回
	logs.Warn("初始化重启定时任务-商城-超时取消")
	go m.restartScheduleExecute()
}

// orderChHandler 通道创建订单
func (m *GoodsManager) orderChHandler() {
	for {
		select {
		case data, ok := <-m.orderCh:
			if ok {
				//4.创建订单
				ctx := context.TODO()
				goods, order, err := m.goodsService.CreateOrder(ctx, data.GoodsOrder)
				if err != nil {
					logs.Error("orderCh 创建订单失败,err:%v", err)
					continue
				}
				order = m.goodsService.OrderInfoByOrderNo(ctx, order.UID, order.OrderNo)
				if order.GoodsType == 1 {
					//5.创建定时任务未支付自动取消
					go m.scheduleExecute(order, MaxOrderPayCancel, data.Session)
				}
				//6.告诉用户订单已生成
				m.manager.ServerMessagePush([]string{strconv.Itoa(int(order.UID))}, proto.GoodsOrderNotifyData(order), data.Session)
				//7.给用户发送商城数量变更的push
				m.OtherUserEntryPush(goods, data.Session)
			}
		}
	}
}

// scheduleExecute 定时关闭未支付的订单
func (m *GoodsManager) scheduleExecute(order *entity.GoodsOrder, times int64, session *remote.Session) {
	m.Lock()
	defer m.Unlock()
	// 触发定时
	if m.schedules[order.Id] != nil {
		m.schedules[order.Id].Stop()
	}
	m.schedules[order.Id] = time.AfterFunc(time.Second*time.Duration(times), func() {
		// 取消定时任务
		timer, ok := m.schedules[order.Id]
		if ok {
			timer.Stop()
		}
		delete(m.schedules, order.Id)
		ctx := context.TODO()
		alipay, err := m.walletService.OrderQueryAlipay(ctx, order.OrderNo, order.Method)
		if err != nil {
			logs.Error("goods manager 自动取消,err=%v", err)
			return
		}
		if alipay.TradeStatus == "TRADE_SUCCESS" {
			order = m.goodsService.OrderInfoByOrderNo(ctx, order.UID, order.OrderNo)
			if order == nil || order.UID == 0 {
				logs.Error("goods manager 自动取消 订单不存在,err=%v", err)
				return
			}
			res, _ := json.Marshal(alipay)
			err = m.goodsService.GoodsPay(ctx, order, string(res))
			if order == nil || order.UID == 0 {
				logs.Error("goods manager 自动取消 完成失败,err=%v", err)
				return
			}
		} else {
			var goods *response.GoodsList
			goods, order, err = m.goodsService.OrderCancel(ctx, order.UID, order.OrderNo)
			if err != nil {
				logs.Error("order timer scheduleExecute,err=%v", goods, err)
				return
			}
			_, ok = m.Users[order.UID]
			if ok {
				//8.推送给用户订单数据
				//fmt.Println(order)
				//m.ServerMessagePush([]string{strconv.Itoa(int(order.UID))}, proto.GoodsOrderNotifyData(order), session)
				//9.恢复商城商品数量
				m.OtherUserEntryPush(goods, session)
			}
		}
		// TODO 钱包自动取消
		//orderData := order.OrderNo[5:13]
		//hfResult, err := m.walletService.OrderQueryWallet(order.OrderNo, orderData)
		//if err != nil {
		//	logs.Error("goods manager 自动取消,err=%v", err)
		//}
		//if hfResult.TransStat == "S" {
		//	order = m.goodsService.OrderInfoByOrderNo(ctx, order.UID, order.OrderNo)
		//	if order == nil || order.UID == 0 {
		//		logs.Error("goods manager 自动取消 订单不存在,err=%v", err)
		//		return
		//	}
		//	response, _ := json.Marshal(hfResult)
		//	err = m.goodsService.GoodsPay(ctx, order, string(response))
		//	if order == nil || order.UID == 0 {
		//		logs.Error("goods manager 自动取消 完成失败,err=%v", err)
		//		return
		//	}
		//} else {
		//	goods, order, err := m.goodsService.OrderCancel(ctx, order.UID, order.OrderNo)
		//	if err != nil {
		//		logs.Error("order timer scheduleExecute,err=%v", goods, err)
		//		return
		//	}
		//	_, ok = m.Users[order.UID]
		//	if ok {
		//		//8.推送给用户订单数据
		//		//fmt.Println(order)
		//		//m.ServerMessagePush([]string{strconv.Itoa(int(order.UID))}, proto.GoodsOrderNotifyData(order), session)
		//		//9.恢复商城商品数量
		//		m.OtherUserEntryPush(goods, session)
		//	}
		//}

	})
}

// scheduleUserExecute 心跳 定时删除用户组里的用户数据
func (m *GoodsManager) scheduleUserExecute(msg *entity.User) {
	m.Lock()
	defer m.Unlock()
	// 触发定时
	t, ok := m.schedulesUser[msg.Uid]
	if ok {
		t.Stop()
		delete(m.schedulesUser, msg.Uid)
	}
	m.schedulesUser[msg.Uid] = time.AfterFunc(time.Second*MaxGoodsWeb, func() {
		// 无更新心跳 关闭定时任务
		timer, ok := m.schedulesUser[msg.Uid]
		if ok {
			timer.Stop()
		}
		delete(m.schedulesUser, msg.Uid)
		// 踢出用户
		delete(m.Users, msg.Uid)
	})
}

func (m *GoodsManager) OtherUserEntryPush(goods *response.GoodsList, session *remote.Session) {
	m.RLock()
	defer m.RUnlock()
	others := make([]string, 0)
	for uid := range m.Users {
		uidStr := strconv.Itoa(int(uid))
		others = append(others, uidStr)
	}
	if session != nil {
		m.manager.ServerMessagePush(others, proto.GoodsNumNotifyData(goods), session)
	}
}

func (m *GoodsManager) OrderPay(uid uint, orderNo string, method string) (string, *msError.Error) {
	//1.查询订单
	ctx := context.TODO()
	order := m.goodsService.OrderInfoByOrderNo(ctx, uid, orderNo)
	if order == nil || order.OrderNo == "" {
		logs.Error("[Goods]用户%d Pay OrderPay err:未获取到订单数据 req=%v", uid, order)
		return "", biz.GoodsOrderError
	}
	if order.GoodsType == 1 {
		times := time.Now().Unix()
		if times > order.CreateTime+MaxOrderPay {
			logs.Error("[Goods]用户%d Pay OrderPay err:已超过操作时间，订单已取消 req=%v,订单时间%v,超时时间%v", uid, order, utils.TimeOrDateAsYear(times, "", "YmdHis"), utils.TimeOrDateAsYear(order.CreateTime+MaxOrderPay, "", "YmdHis"))
			return "", biz.GoodsOrderError
		}
		// TODO 调取支付,获取支付地址
		//wallet := m.userService.GetWalletOfReal(ctx, uid)
		//if wallet == nil || wallet.ID == 0 {
		//	logs.Error("[Goods]用户%d Pay OrderPay err:未获取到钱包数据 订单%v,req=%v", uid, order, wallet)
		//	return "", biz.WalletNotError
		//}
		order.Method = method
		if err := m.goodsService.MongoUpOrderPayMethod(ctx, orderNo, method); err != nil {
			logs.Error("[Goods]用户%d Pay OrderPay err:更新订单支付方式失败 req=%v,payErr=%v", uid, order, err)
			return "", biz.SqlError
		}
		expTime := order.CreateTime + MaxOrderPay
		//var resPay, err = m.walletService.GoodsPay(order.Price, orderNo, wallet.UserCustID, wallet.IDCard, wallet.RealName.Name, wallet.RealName.IDCard, int(expTime))
		var resPay, err = m.walletService.GoodsAliPay(uid, order.Price, orderNo, expTime, order.Method)
		logs.Warn("支付数据为%v", resPay)
		if err != nil {
			logs.Error("[Goods]用户%d Pay OrderPay err:调取支付失败 req=%v,payErr=%v", uid, order, err)
			return "", biz.NotPayError
		}
		return resPay, nil
	} else {
		err := m.goodsService.GoodsPay(ctx, order, "")
		if err != nil {
			logs.Error("[Goods]用户%d Pay GoodsInPay err:积分商城兑换失败 req=%v,payErr=%v", uid, order, err)
			return "", biz.GoodsPointsError
		}
		return "", nil
	}

}

func (m *GoodsManager) restartScheduleExecute() {
	ctx := context.TODO()
	goods := m.goodsService.OvertimeOrderList(ctx)
	for k, v := range *goods {
		data := v
		var times int64
		if time.Now().Unix() >= data.CreateTime+MaxOrderPayCancel {
			//已超时，未处理
			times = int64(k)
		} else {
			times = data.CreateTime + MaxOrderPayCancel - time.Now().Unix()
		}
		//fmt.Println(data, times, utils.TimeAsDate(data.CreateTime), utils.TimeAsDate(data.CreateTime+MaxOrderPayCancel))
		m.scheduleExecute(&data, times, nil)
	}
}

func NewGoodsManager(r *repo.Manager) *GoodsManager {
	g := &GoodsManager{
		orderCh:       make(chan *proto.GoodsRule, 1024),
		schedules:     make(map[primitive.ObjectID]*time.Timer),
		schedulesUser: make(map[uint]*time.Timer),
		goodsService:  service.NewGoodsService(r),
		userService:   service.NewUserService(r),
		walletService: service.NewWalletService(r),
		manager:       NewManager(),
	}
	g.run()
	return g
}
